﻿using System;
using System.Linq;
using System.Text;

namespace Centido.Core
{
    /// <summary>
    ///   A set of table read all operations.
    /// </summary>
    public static class TableReadAllOperations
    {
        /// <summary>
        ///   Reads all rows in the specified data source.
        /// </summary>
        /// <param name="table">The target table.</param>
        /// <param name="source">The target data source.</param>
        /// <param name="readAction">The read action performed on each row.</param>
        /// <param name="values">The list of table column to be read.</param>
        private static void ReadAllRowsImplementation(Table table, DataSource source,
            Action readAction, params ColumnValue[] values)
        {
            // If there is nothing to read
            if (values == null || values.Length == 0)
                throw new ArgumentException("No table columns to be read were specified.");

            var tableName = table.GetDataSourceName(source);

            // List of comma separated column names
            var columnList = new StringBuilder();

            values.ToList().ForEach(
                pair => columnList.Append(columnList.Length != 0 ? "," : string.Empty).Append(pair.ColumnName));

            table.Domain.DbAccess.ExecuteReader(
                cmd =>
                {
                    cmd.CommandText = string.Format(
                        "select {0} from {1}",
                        columnList,
                        tableName);
                },
                reader =>
                {
                    foreach (var value in values.ToList())
                        value.Value = reader[value.ColumnName];

                    if (readAction != null)
                        readAction();
                });
        }


        /// <summary>
        ///   Reads all rows from the table.
        /// </summary>
        /// <remarks>
        /// <para>The row is read from the user view of the internal table.</para>
        /// <para>The current user has to have the select permission on the table in order
        /// to execute this function.</para>
        /// </remarks>
        /// <param name="table">The target table.</param>
        /// <param name="readAction">The read action performed upon each row.</param>
        /// <param name="values">The table column to be read.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when one of the specified values of the table columns is null.</exception>
        /// <exception cref="ArgumentException">Thrown when no table columns were specified.</exception>
        /// <exception cref="System.Data.Common.DbException">Thrown when the current user has no permission to read from the table.</exception>
        /// <example>
        ///   The following example shows how to read all rows from the table.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///             
        ///             domain.DeleteAll();
        ///             
        ///             //Creating a new table
        ///             var table = domain.AddTable("students");
        /// 
        ///             //Granting all necessary permissions
        ///             table.GrantAll();
        /// 
        ///             //Creating two columns
        ///             var surnameColumn = table.AddColumn("surname", ColumnType.String)
        ///                 as StringColumn;
        /// 
        ///             var ageColumn = table.AddColumn("age", ColumnType.Integer)
        ///                 as IntColumn;
        /// 
        ///             //Inserting two rows
        ///             table.InsertRow(surnameColumn.Value("Smith"), ageColumn.Value(22));
        ///             table.InsertRow(surnameColumn.Value("Brown"), ageColumn.Value(21));
        /// 
        ///             //Reading all rows
        ///             var id = table.SystemColumns.Id.EmptyValue();
        ///             var surname = surnameColumn.EmptyValue();
        ///             var age = ageColumn.EmptyValue();
        /// 
        ///             table.ReadAllRows(() =&gt;
        ///             {
        ///                 Console.WriteLine("Student: id = {0}, surname = {1}, age = {2}",
        ///                     id.GetGuid(),
        ///                     surname.GetString(),
        ///                     age.GetInteger());
        ///             }, id, surname, age);
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static void ReadAllRows(this Table table, Action readAction, params ColumnValue[] values)
        {
            if (table == null)
                throw new ArgumentNullException("table");

            ReadAllRowsImplementation(table, DataSource.UserView, readAction, values);
        }


        /// <summary>
        ///   Reads all rows from the specified data source.
        /// </summary>
        /// <remarks>
        /// <para>The current user has to have the select permission on the specified data source in order
        /// to execute this function.</para>
        /// </remarks>
        /// <param name="table">The target table.</param>
        /// <param name="source">The target data source.</param>
        /// <param name="readAction">The read action to be performed upon each row.</param>
        /// <param name="values">The list of table column to be read.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when one of the specified values of the table columns is null.</exception>
        /// <exception cref="ArgumentException">Thrown when no table columns were specified.</exception>
        /// <exception cref="System.Data.Common.DbException">Thrown when the current user has no permission to read from the table.</exception>
        /// <example>
        ///   The following example shows how to read all rows from the specified data source.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///             
        ///             domain.DeleteAll();
        ///             
        ///             //Creating a new table
        ///             var table = domain.AddTable("students");
        /// 
        ///             //Granting all necessary permissions
        ///             table.GrantAll();
        /// 
        ///             //Creating two columns
        ///             var surnameColumn = table.AddColumn("surname", ColumnType.String)
        ///                 as StringColumn;
        /// 
        ///             var ageColumn = table.AddColumn("age", ColumnType.Integer)
        ///                 as IntColumn;
        /// 
        ///             //Inserting two rows
        ///             table.InsertRow(surnameColumn.Value("Smith"), ageColumn.Value(22));
        ///             table.InsertRow(surnameColumn.Value("Brown"), ageColumn.Value(21));
        /// 
        ///             //Reading all rows
        ///             var id = table.SystemColumns.Id.EmptyValue();
        ///             var surname = surnameColumn.EmptyValue();
        ///             var age = ageColumn.EmptyValue();
        /// 
        ///             table.ReadAllRows(DataSource.UserView, () =&gt;
        ///             {
        ///                 Console.WriteLine("Student: id = {0}, surname = {1}, age = {2}",
        ///                     id.GetGuid(),
        ///                     surname.GetString(),
        ///                     age.GetInteger());
        ///             }, id, surname, age);
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static void ReadAllRows(this Table table, DataSource source, Action readAction, params ColumnValue[] values)
        {
            if (table == null)
                throw new ArgumentNullException("table");

            ReadAllRowsImplementation(table, source, readAction, values);
        }
    }
}
